设计模式(二)—6个创建模式

平常所说的工厂模式即工厂方法模式,它是最常用的一种工厂模式。注意,本章的抽象类与接口不做具体区分。

简单工厂模式(simple factory pattern)

定义

定义一个工厂类,创建工厂类的静态(static)方法根据传入参数的不同创建不同的实例,被创建的实例通常具有相同的父类。简单工厂方法又称静态工厂方法。

在没有工厂类之前,客户端通过new关键字创建对象,但有了工厂类之后,通过工厂类中提供的静态方法创建实例。

首先将需要创建的不同对象(具体产品)的相关代码封装到一个抽象类(抽象产品)中,作为这些对象的父类。然后提供一个工厂类用于创建不同的对象产品,在工厂类中提供一个创建不同对象的方法,可以根据不同的传入参数创建不同的对象。

简单工厂模式的3个角色(类)

(1)Factory(工厂角色):即工厂类,它是简单工厂模式的核心,它包含了必要的判断逻辑,根据外界给定的信息(即工厂方法的传入参数),决定应该创建哪个具体类的对象。工厂类可以被外界调用,创建所需的产品对象。在工厂类中提供静态工厂方法factoryMethod(),它的返回类型为抽象产品角色Product。
(2)Procuct(抽象产品角色):它是工厂类所创建的所有具体产品类的父类,封装了各种产品类都公有方法,它的引入提供了系统的灵活性,使得在工厂类中只需要定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
(3)ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标。所有被创建的对象都是这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,所以需要实现抽象产品角色中的抽象方法。

1
2
3
4
5
6
7
8
//抽象类
abstract class Product{
public void methodSame(){
//所有产品类的公共方法
}
//抽象业务方法,不同具体产品有不同实现
public abstract void methodDiff();
}
1
2
3
4
5
6
7
8
9
10
11
12
//具体实现类ConcreteProductA
public ConcreteProductA extends Product{ //若抽象类成员全是抽象的,它就是接口,用implements关键字
public void methodDiff(){
//不同实现类对业务方法有不同实现
}
}
//具体实现类ConcreteProductB
public ConcreteProductB extends Product{
public void methodDiff(){
//不同实现类对业务方法有不同实现
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//工厂类
class Factory{
//静态工厂方法,用于创建不同类的实现对象
public static Product getProduct(String str) {
Product product = null;
if (str.equalsIgnoreCase(“A”)){
product = new ConcreteProductA();
//初始化设置product
} else if (str.equalsIgnoreCase(“A”)){
product = new ConcreteProductA();
//初始化设置product
}
return product;
}
}

在客户端代码中,通过调用工厂类的工厂方法即可得到产品对象,如

1
2
3
4
5
6
7
class Client{
public static void main(String args){
Product product = Factory.getProduct(“A”);
product.methodSame();
produc.methodDiff();
}
}

客户端的重构

每更换一次对象都要修改客户端中静态方法的参数,客户端代码将要重新编译,这对于客户端而言,违反了开闭原则。如何在不修改客户端代码的前提下更换具体产品对象?
可以将工厂类中静态方法的参数放在XML或properties格式的配置文件中。如下config.xml所示:

1
2
3
4
<?xml version=”1.0”?>
<config>
<charType>histogram</charType>
</config>

再通过工具类XMLUtil来读取配置文件中的字符串参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import javax.xml.parsers.*;
import org.xml.sax.SAXException;

public class XMLUtil{
//从XML文件中读取charType并返回类型名
public static String getCharType(){
try{
//创建文档对象
DocumentBuilderFactory dbf = new DocumentBuilderFactory();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File(“config.xml”));
//获取包含charType类型的文本节点
NodeList nl = doc.getElementByTagName(“charType”);
Node n = nl.item().getFirstChild();
String charType = n.getNodeValue().trim();
return charType;
} catch(Exception e){
e.printStackTrace();
return null;
}
}
}

简单工厂模式的简化

有时候为了简化简单工厂模式,可以将抽象产品类和工厂类进行合并,将静态工厂方法转移到抽象产品中。

工厂方法模式(factory method pattern)

简单工厂模式存在两个严重的问题:
(1)工厂类过于庞大,大量if..else语句会造成维护困难;
(2)当系统中需要引进新产品时,由于静态工厂方法通过传入不同的参数来创建不同的产品,这必定要修改工厂类的源代码,需要在其中加入必要的业务逻辑,这将违背“开闭原则”。
如何实现增加新产品而不影响已有代码?(也可以通过上一节提到的将静态方法的参数放在config.xml和propertise文件中实现。)在工厂方法模式中,不再提供统一的工厂类来创建所有的产品对象,而是针对不同的对象提供不同的工厂。(将简单工厂模式中的工厂类抽象化了。)

定义

定义一个用于创建对象的接口(抽象工厂),让子类决定将哪一个类实例化。工厂方法模式让一个列的实例化延迟到其子类。工厂方法模式又叫工厂模式(factory pattern)、虚拟构造器模式(virtual constructor pattern)或多态工厂模式(polymorphic factory pattern)。

工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。

工厂方法模式的4个角色(类)

(1)Product(抽象产品)
它是定义产品的接口,是产品对象的公共父类。
(2)ConcreteProduct(具体产品)
它实现了抽象产品接口,某种类型的具体产品由专门的工厂创建。
(3)Factory(抽象工厂)
声明了一个工厂方法,用于返回具体产品对象。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
(4)ConcreteFactory(具体工厂)
它是抽象工厂的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

1
2
3
4
//抽象工厂
interface Factory{
public Product factoryMethod();
}
1
2
3
4
5
6
7
//具体工厂
class ConcreteFactory implements Factory{
public Product factoryMethod(){
return new ConcreteProduct();
}
//或对象的初始化工作,资源和环境配置工作,如连接数据库,创建文件等。
}
1
2
3
4
//抽象产品
interface Product{

}
1
2
//具体产品

1
2
3
4
5
//客户端代码

Factory factory = new ConcreteFactory(); //实例化具体工厂类
Product product = factory.factoryMethod(); //实例化具体产品类

反射与配置文件

客户端的重构:不再使用new关键字创建工厂对象,而是将具体工厂类的类名存储在配置文件中。通过读取配置文件读取类名字符串,再使用Java的反射机制,根据类名生成字符串对象.

1
2
3
4
//通过类名生成实例对象并返回
Class c = Class.forName(“<类名>”);
Object obj = c.getInstance;
return obj;

重载的工厂方法

可以有多种方法来初始化具体产品,即在具体工厂类中对构造器进行重载。

抽象工厂模式(abstract factory pattern)

工厂方法模式由于每个工厂类只生产一类产品,可能会导致系统中存在大量的工厂类,增加系统开销。考虑将相关的产品组成“产品族”,由同一个工厂来产生。

定义

提供一个创建一些列相关或相互依赖对象的接口,而无需指定它们的具体类。抽象工厂模式又称Kit模式。与工厂方法模式不同的是,抽象工厂模式中的具体工厂不只是创建一种产品,而是创建一族产品。
抽象工厂中定义了多个抽象工厂方法,每一个具体工厂实现了这些工厂方法用于产生多种不同类型的产品,一个工厂类的产品构成了一个产品族

抽象工厂模式包含4种角色(类)

(1)Factory(抽象工厂)
声明了一组方法,用于创建一族产品。每个方法对应一种产品。
(2)ConcreteFactory(具体工厂)
实现了在抽象工厂中声明的创建具体产品的方法,一个具体工厂生成一族产品,构成一个产品族。
(3)Product(抽象产品)
为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
(4)ConcreteProduct(具体产品)
定义具体工厂生产的具体产品对象,实现抽闲产品中声明的业务方法。

1
2
3
4
5
//抽象工厂
abstract class Factory{
public abstract ProductA createProductA();
puclib abstract ProductB createProductB();
}
1
2
3
4
5
6
7
8
9
//具体工厂类1
class ConcreteFactory1 extends Factory{
public ProductA createProductA(){
return new ConcreteProductA1();
}
puclib ProductB createProductB(){
return new ConcreteProductB1();
}
}
1
2
3
4
5
6
7
8
9
//具体工厂类2
class ConcreteFactory2 extends Factory{
public ProductA createProductA(){
return new ConcreteProductA2();
}
puclib ProductB createProductB(){
return new ConcreteProductB2();
}
}
1
//抽象产品
1
//具体产品
1
//客户端类

为了让系统局部良好的灵活性和可扩展性,引入XML配置文件和XMLUtil工具类。

“开闭原则”的倾斜性

在抽象工厂模式中,增加新的产品族(即增加一个具体产品类)很方便,但增加新的产品等级结构(即在一个工厂类中增加方法)很麻烦,需要修改所欲的工厂角色。这种性质称为“开闭原则”的倾斜性。

单例模式(singleton pattern)

定义

单例就是保证始终只有一个实例。确保对象的唯一性,如Windows的任务管理器,打印控制器,数据库连接池等。实现单例注意点:
(1)构造器私有,外部无法创建实例;
(2)因此需要提供公有的方法来获取实例getInstance();
(3)静态类私有成员变量,保证只有一个变量引用。

单例模式的两种实现形式

(1)懒汉式:在第一次获取实例时才创建对象。
如果在获取实例前线程A暂停了,此时线程B获取实例并创建了对象并返回了,然后线程A恢复,又创建了一个对象,不能保证单例,因此需要为getInstance()方法加上synchronized关键字保证线程同步。

1
2
3
4
5
6
7
8
private static ConObject obj = null;
private ConObject (){} //私有构造器
public static synchronized ConObject getInstance(){
if (obj == null) { //创建对象前保证只有一个对象
obj = new ConObject ();
}
return obj;
}

(2)饿汉式:在类加载时就已经创建对象了。
饿汉式不存在线程安全问题,但容易造成资源浪费,因为实例在类加载的时候随着静态变量的初始化而创建,但有时候可能并不会使用该实例。

1
2
3
4
5
private static ConObject obj = new ConObject ();
private ConObject (){}
public static ConObject getInstance(){
return obj;
}

原型模式(prototype pattern)

建造者模式(builder pattern)